package com.ccf.business.source.controller;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.file.FileNameUtil;
import cn.hutool.core.map.MapBuilder;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.ZipUtil;
import com.common.utils.constant.Constants;
import com.ccf.business.source.model.Source;
import com.ccf.business.source.model.vo.FolderVO;
import com.ccf.business.source.model.vo.UploadVO;
import com.ccf.business.source.service.ISourceService;
import com.common.model.result.Result;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.List;

/**
 * 文件中心
 *
 * @author ccf
 * @date 2022-02-15 14:20:52
 */
@Slf4j
@RestController
@RequestMapping("/file")
@Api(tags = "文件中心")
@RequiredArgsConstructor
public class SourceController {

    private final ISourceService fileService;

    /**
     * 列表
     */
//    @ApiOperation(value = "分页查询列表")
//    @GetMapping
//    public PageResult list(Query query) {
//        return fileService.findList(query);
//    }

    @ApiOperation(value = "文件目录列表")
    @ApiImplicitParam(value = "关键字", name = "name", required = false)
    @GetMapping("/folderList")
    public Result folderList(@RequestParam(value = "name",required = false) String name) {
        List<FolderVO> folderVOS = fileService.folderList(name);
        return Result.succeed(folderVOS, "查询成功");
    }

    /**
     * 列表
     */
    @ApiOperation(value = "文件列表", notes = "fileTypes可以进行文件类型筛选，传递pdf,doc,docx,txt")
    @ApiImplicitParams({
        @ApiImplicitParam(value = "父文件夹id", name = "pid", required = true),
        @ApiImplicitParam(value = "文件名称", name = "name", required = false),
        @ApiImplicitParam(value = "文件类型(模糊查询)", name = "fileType", required = false),
        @ApiImplicitParam(value = "文件类型(多个逗号分隔 分类查询)", name = "fileTypes", required = false)
    })
    @GetMapping("/fileList")
    public Result fileList(@RequestParam(value = "pid", required = true) String pid,
                           @RequestParam(value = "name", required = false) String name,
                           @RequestParam(value = "fileType", required = false) String fileType,
                           @RequestParam(value = "fileTypes", required = false) String fileTypes) {
        List<Source> sources = fileService.fileList(pid, name, fileType, fileTypes);
        return Result.succeed(sources, "查询成功");
    }
    /**
     * 列表
     */
    @ApiOperation(value = "上传列表", notes = "正在上传中的文件给前端管理")
    @GetMapping("/uploadList")
    public Result uploadList() {
        List<UploadVO> sources = fileService.uploadList();
        return Result.succeed(sources, "查询成功");
    }


    /**
     * 查询
     */
    @ApiOperation(value = "查询")
    @GetMapping("/{id}")
    public Result getById(@PathVariable String id) {
        Source model = fileService.detail(id);
        return Result.succeed(model, "查询成功");
    }

    /**
     * 新增or更新
     */
    @ApiOperation(value = "新建文件夹/重命名", notes = "新建文件夹name，pid；文件重命名传递id，name")
    @PostMapping
    public Result save(@RequestBody Source file) {
        if (StrUtil.isBlank(file.getPid())){
            return Result.failed("pid参数是必须项");
        }
        String fileName = file.getName();
        if (FileNameUtil.containsInvalid(fileName) ){
            return Result.failed("文件名称包含非法字符");
        }
        if (fileName.length() > Source.MAX_FILE_NAME_LENGTH){
            return Result.failed("文件名称过长");
        }
        boolean existByMap = fileService.isExistByMap(file.getId(), file.getName(), "name",
                MapBuilder.<String, Object>create().put("pid", file.getPid()).build());
        if (existByMap){
            int i = 1;
            file.setName(recursion(file.getName(), file.getId() , file.getPid(), i));
        }
        fileService.saveOrRename(file);
        return Result.succeed("保存成功");
    }


    /**
     * 递归返回最新名称
     * @return
     */
    private String recursion(String name, String id, String pid, int i) {
        boolean existByMap = fileService.isExistByMap(id, name + i, "name",
                MapBuilder.<String, Object>create().put("pid", pid).build());
        if (existByMap){
            return recursion(name, id , pid, i+1);
        }
        return name + i;
    }

    /**
     * 移动
     */
    @ApiOperation(value = "移动")
    @ApiImplicitParams({
            @ApiImplicitParam(value = "需要移动的文件或文件夹id,多个用逗号分隔", name="id", required = true),
            @ApiImplicitParam(value = "移动到的文件夹id", name="pid", required = true)
    })
    @PostMapping("/move")
    public Result move(@RequestBody Source file) {
        if (StrUtil.isBlank(file.getId()) || StrUtil.isBlank(file.getPid())){
            return Result.failed("必要参数为空");
        }
        String[] idArray = StrUtil.split(file.getId(), Constants.COMMA);
        if (ArrayUtil.contains(idArray, file.getPid())){
            return Result.failed("移动文件中不能包含移动目标文件夹");
        }
        fileService.move(idArray, file.getPid());
        return Result.succeed("保存成功");
    }

    /**
     * 删除
     */
    @ApiOperation(value = "删除")
    @DeleteMapping("/{id}")
    public Result delete(@PathVariable String id) {
        //批量删除
        if (StrUtil.contains(id, ",")){
            String[] ids = StrUtil.split(id, ",");
            fileService.removeAndCacheByIds(ids);
        } else {
            fileService.removeAndCacheByIds(id);
        }
        return Result.succeed("删除成功");
    }


    /**
     * 上传多个文件
     */
    @ApiOperation(value = "上传",notes = "上传多个文件,根目录pid传递“0”")
    @PostMapping("/upload")
    public Result upload(@RequestParam("file") MultipartFile file,
                         @RequestParam(value = "pid", required = true) String pid,
                         HttpServletRequest request) {
        try {
            fileService.upload(new MultipartFile[]{file}, pid);
            List<Source> sources = fileService.fileList(pid,null,null,null);
            return Result.succeed(sources, "提交成功");
        } catch (Exception e) {
            e.printStackTrace();
            return Result.failed("上传失败");
        }
    }

    /**
     * 上传多个文件
     */
    @ApiOperation(value = "上传多文件",notes = "上传多个文件,根目录pid传递“0”")
    @PostMapping("/uploads")
    public Result uploads(@RequestParam("files") MultipartFile[] files,
                         @RequestParam(value = "pid", required = true) String pid,
                         HttpServletRequest request) {
        try {
            fileService.upload(files, pid);
            return Result.succeed("提交成功");
        } catch (Exception e) {
            e.printStackTrace();
            return Result.failed("上传失败");
        }
    }

//    /**
//     * 提交多个文件
//     */
//    @ApiOperation(value = "提交",notes = "先提交在上传")
//    @PostMapping("/submit")
//    public Result submit(@RequestParam("files") MultipartFile[] files,
//                         @RequestParam(value = "pid", required = true) String pid,
//                         HttpServletRequest request) {
//        try {
//            fileService.upload(files, pid);
//            return Result.succeed("提交成功");
//        } catch (Exception e) {
//            e.printStackTrace();
//            return Result.failed("上传失败");
//        }
//    }


    /**
     * 下载多个文件
     */
    @ApiOperation(value = "下载",notes = "多文件用逗号分隔")
    @GetMapping("/download/{id}")
    public void download(@PathVariable String id, HttpServletResponse response) {
        File dir = fileService.packageToDir(id);
        if (dir == null) {
            log.error("文件获取失败");
            return;
        }
        //文件夹
        if (dir.isDirectory()){
            try (ServletOutputStream outputStream = response.getOutputStream()) {
                response.reset();
                response.setCharacterEncoding("utf-8");
                String fileName = URLEncoder.encode(dir.getName()+".zip", "utf-8");
                response.setHeader("Content-Disposition", "attachment;filename="+fileName+";filename*=utf-8''"
                        + fileName);
                response.addHeader("Content-type", "application/zip");
                response.setContentType("application/zip");
                ZipUtil.zip(outputStream, CharsetUtil.CHARSET_UTF_8, true,
                        e->true, dir);
            } catch (Throwable e) {
                e.printStackTrace();
                log.error("文件下载异常"+e.getMessage());
            }
        } else {
            downloadFile(dir, response);
        }
        FileUtil.del(dir);
    }

    /**
     * 下载文件
     *
     * @param response HttpServletResponse
     */
    public void downloadFile(File file, HttpServletResponse response) {
        BufferedInputStream bis = FileUtil.getInputStream(file);
        String fileName = file.getName();
        try {
            response.reset();
            response.setCharacterEncoding("utf-8");
            response.setContentType("application/octet-stream");
            String fileNameCode = URLEncoder.encode(fileName, "utf-8");
            response.setHeader("Content-Disposition", "attachment;filename="+ fileNameCode +";filename*=utf-8''"
                    + fileNameCode);
            response.setContentLength(bis.available());

            IOUtils.copy(bis, response.getOutputStream());
        } catch (Exception e) {
            e.printStackTrace();
            log.error("文件下载异常"+e.getMessage());
        } finally {
            try {
                if (response.getOutputStream() != null) {
                    response.getOutputStream().close();
                }
                if (bis != null) {
                    bis.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
